//************************************************************************
// author	:	vera												
// date		:	2011/4/7 0:03 					                 
//************************************************************************
#ifndef SL_MATRIX4_H_
#define SL_MATRIX4_H_
#include "Math.h"
#include "Config.h"
#include "Vector3.h"
namespace sl
{
	class SL_EXPORT Matrix4
	{
		typedef float value_type;

	protected:

		union {
			value_type	m[4][4];
			value_type	_m[16];
		};

	public:
		Matrix4(void)
		{
			for ( u32 i = 0; i < 16; ++i )
			{
				_m[i] = 0.0f;
			}
		};

		Matrix4(const Matrix4 & m )
		{

		}

		Matrix4(const float fValue )
		{
			for ( u32 i = 0; i < 16; ++i )
			{
				_m[i] = fValue;
			}
		}

		Matrix4( value_type a00, value_type a01, value_type a02, value_type a03,
				value_type a10, value_type a11, value_type a12, value_type a13,
				value_type a20, value_type a21, value_type a22, value_type a23,
				value_type a30, value_type a31, value_type a32, value_type a33 )
		{
			m[0][0] = a00;
			m[0][1] = a01;
			m[0][2] = a02;
			m[0][3] = a03;
			m[1][0] = a10;
			m[1][1] = a11;
			m[1][2] = a12;
			m[1][3] = a13;
			m[2][0] = a20;
			m[2][1] = a21;
			m[2][2] = a22;
			m[2][3] = a23;
			m[3][0] = a30;
			m[3][1] = a31;
			m[3][2] = a32;
			m[3][3] = a33;
		}

		~Matrix4(void)
		{

		}

		inline void set( const Matrix4 & rhs )
		{
			for(int i=0;i<16;++i) _m[i]=rhs._m[i];
		}

		inline value_type & operator() ( u32 row, u32 col )			{ return m[row][col]; }
		inline value_type	operator() ( u32 row, u32 col ) const	{ return m[row][col]; }
 
		// define the [][] operator
		inline value_type * operator[]( u32 row ) { return m[row];}

		inline Matrix4 operator + ( const Matrix4 &m2 ) const
		{
			Matrix4 r;

			r.m[0][0] = m[0][0] + m2.m[0][0];
			r.m[0][1] = m[0][1] + m2.m[0][1];
			r.m[0][2] = m[0][2] + m2.m[0][2];
			r.m[0][3] = m[0][3] + m2.m[0][3];

			r.m[1][0] = m[1][0] + m2.m[1][0];
			r.m[1][1] = m[1][1] + m2.m[1][1];
			r.m[1][2] = m[1][2] + m2.m[1][2];
			r.m[1][3] = m[1][3] + m2.m[1][3];

			r.m[2][0] = m[2][0] + m2.m[2][0];
			r.m[2][1] = m[2][1] + m2.m[2][1];
			r.m[2][2] = m[2][2] + m2.m[2][2];
			r.m[2][3] = m[2][3] + m2.m[2][3];

			r.m[3][0] = m[3][0] + m2.m[3][0];
			r.m[3][1] = m[3][1] + m2.m[3][1];
			r.m[3][2] = m[3][2] + m2.m[3][2];
			r.m[3][3] = m[3][3] + m2.m[3][3];

			return r;
		}

		inline Matrix4 operator - ( const Matrix4 &m2 ) const
		{
			Matrix4 r;
			r.m[0][0] = m[0][0] - m2.m[0][0];
			r.m[0][1] = m[0][1] - m2.m[0][1];
			r.m[0][2] = m[0][2] - m2.m[0][2];
			r.m[0][3] = m[0][3] - m2.m[0][3];

			r.m[1][0] = m[1][0] - m2.m[1][0];
			r.m[1][1] = m[1][1] - m2.m[1][1];
			r.m[1][2] = m[1][2] - m2.m[1][2];
			r.m[1][3] = m[1][3] - m2.m[1][3];

			r.m[2][0] = m[2][0] - m2.m[2][0];
			r.m[2][1] = m[2][1] - m2.m[2][1];
			r.m[2][2] = m[2][2] - m2.m[2][2];
			r.m[2][3] = m[2][3] - m2.m[2][3];

			r.m[3][0] = m[3][0] - m2.m[3][0];
			r.m[3][1] = m[3][1] - m2.m[3][1];
			r.m[3][2] = m[3][2] - m2.m[3][2];
			r.m[3][3] = m[3][3] - m2.m[3][3];

			return r;
		}

		inline bool operator == ( const Matrix4& m2 ) const
		{
			if( 
				m[0][0] != m2.m[0][0] || m[0][1] != m2.m[0][1] || m[0][2] != m2.m[0][2] || m[0][3] != m2.m[0][3] ||
				m[1][0] != m2.m[1][0] || m[1][1] != m2.m[1][1] || m[1][2] != m2.m[1][2] || m[1][3] != m2.m[1][3] ||
				m[2][0] != m2.m[2][0] || m[2][1] != m2.m[2][1] || m[2][2] != m2.m[2][2] || m[2][3] != m2.m[2][3] ||
				m[3][0] != m2.m[3][0] || m[3][1] != m2.m[3][1] || m[3][2] != m2.m[3][2] || m[3][3] != m2.m[3][3] )
				return false;
			return true;
		}

		inline bool operator != ( const Matrix4& m2 ) const
		{
			if( 
				m[0][0] != m2.m[0][0] || m[0][1] != m2.m[0][1] || m[0][2] != m2.m[0][2] || m[0][3] != m2.m[0][3] ||
				m[1][0] != m2.m[1][0] || m[1][1] != m2.m[1][1] || m[1][2] != m2.m[1][2] || m[1][3] != m2.m[1][3] ||
				m[2][0] != m2.m[2][0] || m[2][1] != m2.m[2][1] || m[2][2] != m2.m[2][2] || m[2][3] != m2.m[2][3] ||
				m[3][0] != m2.m[3][0] || m[3][1] != m2.m[3][1] || m[3][2] != m2.m[3][2] || m[3][3] != m2.m[3][3] )
				return true;
			return false;
		}

		inline Matrix4 transpose(void) const
		{
			return Matrix4(m[0][0], m[1][0], m[2][0], m[3][0],
				m[0][1], m[1][1], m[2][1], m[3][1],
				m[0][2], m[1][2], m[2][2], m[3][2],
				m[0][3], m[1][3], m[2][3], m[3][3]);
		}

		inline void setTrans( const Vector3& v )
		{
			m[0][3] = v.x();
			m[1][3] = v.y();
			m[2][3] = v.z();
		}

		inline Vector3 getTrans() const
		{
			return Vector3(m[0][3], m[1][3], m[2][3]);
		}

		inline void preMultTranslate( const Vector3& v )
		{
			for ( u32 i = 0; i < 3; ++i )
			{
				double tmp = v[i];
				if ( tmp == 0 )
					continue;

				m[3][0] += tmp * m[i][0];
				m[3][1] += tmp * m[i][1];
				m[3][2] += tmp * m[i][2];
			}
		}

		inline void makeTrans( const Vector3& v )
		{
			m[0][0] = 1.0; m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = v.x();
			m[1][0] = 0.0; m[1][1] = 1.0; m[1][2] = 0.0; m[1][3] = v.y();
			m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = 1.0; m[2][3] = v.z();
			m[3][0] = 0.0; m[3][1] = 0.0; m[3][2] = 0.0; m[3][3] = 1.0;
		}

		inline void makeTrans( value_type tx, value_type ty, value_type tz )
		{
			m[0][0] = 1.0; m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = tx;
			m[1][0] = 0.0; m[1][1] = 1.0; m[1][2] = 0.0; m[1][3] = ty;
			m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = 1.0; m[2][3] = tz;
			m[3][0] = 0.0; m[3][1] = 0.0; m[3][2] = 0.0; m[3][3] = 1.0;
		}

		inline static Matrix4 getTrans( const Vector3& v )
		{
			Matrix4 r;

			r.m[0][0] = 1.0; r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = v.x();
			r.m[1][0] = 0.0; r.m[1][1] = 1.0; r.m[1][2] = 0.0; r.m[1][3] = v.y();
			r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = 1.0; r.m[2][3] = v.z();
			r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

			return r;
		}

		inline static Matrix4 getTrans( value_type t_x, value_type t_y, value_type t_z )
		{
			Matrix4 r;

			r.m[0][0] = 1.0; r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = t_x;
			r.m[1][0] = 0.0; r.m[1][1] = 1.0; r.m[1][2] = 0.0; r.m[1][3] = t_y;
			r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = 1.0; r.m[2][3] = t_z;
			r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

			return r;
		}

		inline void setScale( const Vector3& v )
		{
			m[0][0] = v.x();
			m[1][1] = v.y();
			m[2][2] = v.z();
		}

	
		inline static Matrix4 getScale( const Vector3& v )
		{
			Matrix4 r;
			r.m[0][0] = v.x(); r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = 0.0;
			r.m[1][0] = 0.0; r.m[1][1] = v.y(); r.m[1][2] = 0.0; r.m[1][3] = 0.0;
			r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = v.z(); r.m[2][3] = 0.0;
			r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

			return r;
		}

		inline static Matrix4 getScale( value_type s_x, value_type s_y, value_type s_z )
		{
			Matrix4 r;
			r.m[0][0] = s_x; r.m[0][1] = 0.0; r.m[0][2] = 0.0; r.m[0][3] = 0.0;
			r.m[1][0] = 0.0; r.m[1][1] = s_y; r.m[1][2] = 0.0; r.m[1][3] = 0.0;
			r.m[2][0] = 0.0; r.m[2][1] = 0.0; r.m[2][2] = s_z; r.m[2][3] = 0.0;
			r.m[3][0] = 0.0; r.m[3][1] = 0.0; r.m[3][2] = 0.0; r.m[3][3] = 1.0;

			return r;
		}

		inline Matrix4 operator * (value_type scalar) const
		{
			return Matrix4(
				scalar*m[0][0], scalar*m[0][1], scalar*m[0][2], scalar*m[0][3],
				scalar*m[1][0], scalar*m[1][1], scalar*m[1][2], scalar*m[1][3],
				scalar*m[2][0], scalar*m[2][1], scalar*m[2][2], scalar*m[2][3],
				scalar*m[3][0], scalar*m[3][1], scalar*m[3][2], scalar*m[3][3]);
		}

		// base the graphic math in 3d space
		inline Matrix4 operator * ( Matrix4 & m2 )
		{
			Matrix4 r;
			r.m[0][0] = m[0][0] * m2.m[0][0] + m[0][1] * m2.m[1][0] + m[0][2] * m2.m[2][0] + m[0][3] * m2.m[3][0];
			r.m[0][1] = m[0][0] * m2.m[0][1] + m[0][1] * m2.m[1][1] + m[0][2] * m2.m[2][1] + m[0][3] * m2.m[3][1];
			r.m[0][2] = m[0][0] * m2.m[0][2] + m[0][1] * m2.m[1][2] + m[0][2] * m2.m[2][2] + m[0][3] * m2.m[3][2];
			r.m[0][3] = m[0][0] * m2.m[0][3] + m[0][1] * m2.m[1][3] + m[0][2] * m2.m[2][3] + m[0][3] * m2.m[3][3];

			r.m[1][0] = m[1][0] * m2.m[0][0] + m[1][1] * m2.m[1][0] + m[1][2] * m2.m[2][0] + m[1][3] * m2.m[3][0];
			r.m[1][1] = m[1][0] * m2.m[0][1] + m[1][1] * m2.m[1][1] + m[1][2] * m2.m[2][1] + m[1][3] * m2.m[3][1];
			r.m[1][2] = m[1][0] * m2.m[0][2] + m[1][1] * m2.m[1][2] + m[1][2] * m2.m[2][2] + m[1][3] * m2.m[3][2];
			r.m[1][3] = m[1][0] * m2.m[0][3] + m[1][1] * m2.m[1][3] + m[1][2] * m2.m[2][3] + m[1][3] * m2.m[3][3];

			r.m[2][0] = m[2][0] * m2.m[0][0] + m[2][1] * m2.m[1][0] + m[2][2] * m2.m[2][0] + m[2][3] * m2.m[3][0];
			r.m[2][1] = m[2][0] * m2.m[0][1] + m[2][1] * m2.m[1][1] + m[2][2] * m2.m[2][1] + m[2][3] * m2.m[3][1];
			r.m[2][2] = m[2][0] * m2.m[0][2] + m[2][1] * m2.m[1][2] + m[2][2] * m2.m[2][2] + m[2][3] * m2.m[3][2];
			r.m[2][3] = m[2][0] * m2.m[0][3] + m[2][1] * m2.m[1][3] + m[2][2] * m2.m[2][3] + m[2][3] * m2.m[3][3];

			r.m[3][0] = m[3][0] * m2.m[0][0] + m[3][1] * m2.m[1][0] + m[3][2] * m2.m[2][0] + m[3][3] * m2.m[3][0];
			r.m[3][1] = m[3][0] * m2.m[0][1] + m[3][1] * m2.m[1][1] + m[3][2] * m2.m[2][1] + m[3][3] * m2.m[3][1];
			r.m[3][2] = m[3][0] * m2.m[0][2] + m[3][1] * m2.m[1][2] + m[3][2] * m2.m[2][2] + m[3][3] * m2.m[3][2];
			r.m[3][3] = m[3][0] * m2.m[0][3] + m[3][1] * m2.m[1][3] + m[3][2] * m2.m[2][3] + m[3][3] * m2.m[3][3];

			return r;
		}

		inline bool isIdentity() const 
		{
			return	m[0][0]==1.0f && m[0][1]==0.0f && m[0][2]==0.0f &&  m[0][3]==0.0f &&
					m[1][0]==0.0f && m[1][1]==1.0f && m[1][2]==0.0f &&  m[1][3]==0.0f &&
					m[2][0]==0.0f && m[2][1]==0.0f && m[2][2]==1.0f &&  m[2][3]==0.0f &&
					m[3][0]==0.0f && m[3][1]==0.0f && m[3][2]==0.0f &&  m[3][3]==1.0f;
		}

		inline void makeFrustum( double left, double right, double top, double bottom, double zNear, double zFar )
		{
			double tx = -( right + left )/( right - left );
			double ty = -( top + bottom )/( top - bottom );
			double tz = -( zFar + zNear )/( zFar - zNear );
			m[0][0] = 2.0/(right-left); m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = 0.0;
			m[1][0] = 0.0;	m[1][1] = 2.0/(top-bottom); m[1][2] = 0.0; m[1][3] = 0.0;
			m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = -2.0/(zFar-zNear); m[2][3] = 0.0;
			m[3][0] = tx; m[3][1] = ty; m[3][2] = tz; m[3][3] = 1.0;

		}

		inline void makePerspective( double fov, double aspactRatio, double zNear, double zFar )
		{
			double tan_fov = tan( sl::RadiansToDegrees( fov ) * 0.5 );
			double right	=	tan_fov * zNear * aspactRatio;
			double left		=	-right;
			double top		=	tan_fov * zNear;
			double bottom	=	-top;
			makeFrustum( left, right, top, bottom, zNear, zFar );
		}

		inline void makeLookAt( const Vector3& eye, const Vector3 center, const Vector3 up )
		{
			Vector3 f(center-eye);
			f.normalize();
			Vector3 s(f^up);
			s.normalize();
			Vector3 u(s^f);
			s.normalize();

			m[0][0] = s[0]; m[0][1] = u[0]; m[0][2] = -f[0]; m[0][3] = 0.0;
			m[1][0] = s[1];	m[1][1] = u[1]; m[1][2] = -f[1]; m[1][3] = 0.0;
			m[2][0] = s[2]; m[2][1] = u[2]; m[2][2] = -f[2]; m[2][3] = 0.0;
			m[3][0] = 0.0;  m[3][1] = 0.0;  m[3][2] = 0.0;  m[3][3] = 1.0;

			preMultTranslate(-eye);
		}


		inline static Matrix4 perspective( double fov, double aspactRatio, double zNear, double zFar )
		{
			Matrix4 m;
			m.makePerspective( fov, aspactRatio, zNear, zFar );
			return m;
		}

		inline static Matrix4 lookAt( const Vector3& eye, const Vector3 center, const Vector3 up )
		{
			Matrix4 m;
			m.makeLookAt( eye, center, up );
			return m;
		}

		//inline void makeIdentity();
	
		//inline void makeRotate( const Vector3& from, const Vector3& to );
		//inline void makeRotate( value_type angle, const Vector3& axis );
		//inline void makeRotate( value_type angle, value_type x, value_type y, value_type z );
		//inline void makeRotate( value_type angle1, const Vector3& axis1, 
		//	value_type angle2, const Vector3& axis2,
		//	value_type angle3, const Vector3& axis3);
	
		//inline void makeOrtho(double left,   double right,
		//						double bottom, double top,
		//						double zNear,  double zFar);

		//inline bool getOrtho(double& left,   double& right,
		//						double& bottom, double& top,
		//						double& zNear,  double& zFar) const;

		//inline void makeFrustum(double left,   double right,
		//						double bottom, double top,
		//						double zNear,  double zFar);

		//inline bool getFrustum(double& left,   double& right,
		//						double& bottom, double& top,
		//						double& zNear,  double& zFar) const;

		//inline void makePerspective(double fovy,  double aspectRatio,
		//						double zNear, double zFar);

		//inline bool getPerspective(double& fovy,  double& aspectRatio,
		//						double& zNear, double& zFar) const;
  //  
  //      inline void getLookAt(Vector3& eye,Vector3& center,Vector3& up,
		//						value_type lookDistance=1.0f) const;


		////basic utility functions to create new matrices
		//inline static Matrix4 identity( void );
		//inline static Matrix4 scale( const Vector3& sv);
		//inline static Matrix4 scale( value_type sx, value_type sy, value_type sz);
		//inline static Matrix4 translate( const Vector3& dv);
		//inline static Matrix4 translate( value_type x, value_type y, value_type z);
		//inline static Matrix4 rotate( const Vector3& from, const Vector3& to);
		//inline static Matrix4 rotate( value_type angle, value_type x, value_type y, value_type z);
		//inline static Matrix4 rotate( value_type angle, const Vector3& axis);
		//inline static Matrix4 rotate( value_type angle1, const Vector3& axis1, 
		//value_type angle2, const Vector3& axis2,
		//value_type angle3, const Vector3& axis3);
		//inline static Matrix4 inverse( const Matrix4& matrix);
		//inline static Matrix4 orthoNormal(const Matrix4& matrix); 

		//inline static Matrix4 ortho(double left,   double right,
		//	double bottom, double top,
		//	double zNear,  double zFar);

		//inline static Matrix4 ortho2D(double left,   double right,
		//	double bottom, double top);

		//inline static Matrix4 frustum(double left,   double right,
		//	double bottom, double top,
		//	double zNear,  double zFar);

		//inline static Matrix4 perspective(double fovy,  double aspectRatio,
		//	double zNear, double zFar);


		//inline static Matrix4 lookAt(const Vector3& eye,
		//	const Vector3& center,
		//	const Vector3& up);


	};

}

#endif
